iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 5
8
Modern Web

從 Hooks 開始,讓你的網頁 React 起來系列 第 5

[Day 05 - 計數器] 將計數器頁面改成用 JSX 來寫

  • 分享至 

  • xImage
  •  

感謝 iT 邦幫忙與博碩文化,本系列文章已出版成書「從 Hooks 開始,讓你的網頁 React 起來」,首刷版稅將全額贊助 iT 邦幫忙鐵人賽,歡迎前往購書,鼓勵筆者撰寫更多優質文章。

在 React 18 後已經棄用 ReactDOM.render(),改用 ReactDOM.createRoot(),內文中的圖片並未一併修改,煩請讀者留意。

Day 4 - 把 HTML 寫在 JavaScript 中有什麼好處? 中,已經知道怎麼把 HTML 放入 JSX 中呈現出來,今天我們要把 Day 03 - 用原生 JavaScript 做一個簡單的計數器 完成的計數器一樣放入到 JSX 中,除了學習如何在 JSX 中帶入 CSS 樣式之外,也會用這個計數器來實作更多 React 用得到的功能。

今天的重點包含:

  • 建立第一個 React 元件
  • 在 JSX 中帶入 CSS 樣式
  • 在 JSX 中帶入 inline-style
  • 使用元件的好處

將計數器的 HTML 放入 JSX 內

一開始先從 React Hello World Template 開始,把這個專案 Fork 一份來做修改:

Imgur

接下來把 Day 3 中完成的 Counter Started Template ,將 CSS 的部分複製進剛剛 Fork 過來的專案,這時候的畫面應該會像這樣:

Imgur

再來則把 Counter Started Template 中 HTML 的部分複製進剛剛 Fork 的專案中,要放在 JSX 的地方,而不是直接貼到 HTML 內,像這樣:

Imgur

這時候看起來已經把 HTML 放到 JavaScript 中透過 JSX 來呈現出畫面了。

我們也可以把 JSX 的內容抽成一個 JavaScript 變數,只需要把 JSX 的內容用小括號(())包起來就好,像是這樣:

// JSX 的內容可以放到 () 內當成變數
const Counter = (
  <div class="container">
    <div class="chevron chevron-up" />
    <div class="number">256</div>
    <div class="chevron chevron-down" />
  </div>
);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(Counter);

在 JSX 中套入 CSS 樣式

使用 class 樣式

現在雖然我們從 CodePen 看畫面都正常,撰寫的 CSS 樣式也都有出來,但實際上打開 console 視窗你會看到一個錯誤訊息:

Imgur

裡面寫了:

Warning: Invalid DOM property `class`. Did you mean `className`?

之所以會有這個錯誤訊息,是因為在 JavaScript 中,class 本身就已經是個關鍵字,它主要是用來定義類別(class)用的,因此為了避免踩到 JavaScript 的這個關鍵字,在 JSX 中就把原本 HTML 中使用的 class 都改用 className,程式碼會變成:

// JS
// 避免關鍵字衝突,在 JSX 中把原本的 CSS class 都改成用 className
const Counter = (
  <div className="container">
    <div className="chevron chevron-up" />
    <div className="number">256</div>
    <div className="chevron chevron-down" />
  </div>
);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(Counter);

使用 inline-style(行內樣式)

如果我們需要在 JSX 中撰寫 inline-style 的話,可以在 HTML 標籤內的 style 屬性中以帶入物件的方式來完成

在前面我們有提到,在 JSX 中可以使用 {} 來帶入變數,當我們想要撰寫 inline-style 時,就可以在 <div style={} >{} 中放入物件,物件的屬性名稱會是 CSS 的屬性,但會用「小寫駝峰」來表示;屬性值則是 CSS 的值,具體的寫法會像這樣:

// 定義 inline-style 行內樣式
const someStyle = {
  backgroundColor: white,
  fontSize: '20px',          // 也可以寫 20,引號和 px 可以省略
  border: '1px solid white',
  padding: 10,               // 省略 px,樣式會自動帶入單位變成 '10px'
}

// 在 style 中帶入物件,即可撰寫出 inline-style
const SomeElement = (
  <div style={someStyle} />
)

以計數器的範例來說,可以先定義一個名為 shadow 的物件,裡面放入 CSS,接著再把它帶入 JSX 的 style 屬性中:

// JavaScript
// 定義 inline-style 行內樣式
const shadow = {
  boxShadow: '0 0 10px 10px #eaeaea',
  padding: 20, // 省略 px,樣式會自動帶入單位變成 '20px'
};

// 在 style 中帶入物件,即可撰寫出 inline-style
const Counter = (
  // 將行內樣式帶入 `style` 內
  <div className="container" style={shadow}>
    <div className="chevron chevron-up" />
    <div className="number">256</div>
    <div className="chevron chevron-down" />
  </div>
);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(Counter);

如此就可以套用行內樣式:

Imgur

許多時候,開發者可能不會先定義一個 inline-style 的物件,接著才放入 JSX 的 style 屬性內,而是 直接把 inline-style 這個物件寫在 style={}{} 內, 程式碼第一眼看起來會覺得很奇怪,但實際上就只是在 style={}{} 內再放入一個物件而已:

const Counter = (
  // ...
  <div
    className="number"
    // 直接把定義 inline-style 的物件,放到 style={} 的 {} 內
    style={{
      color: '#FFE8E8',
      textShadow: '2px 2px #434a54',
    }}
  >
    256
  </div>
  // ...
);

Imgur

關於 CSS 樣式的添加的程式碼如果不太清楚的話,可以參考 CodePen 上 Day 5 - Counter with inline-style and JSX @ CodePen

建立第一個 React 元件

建立 React 元件

在 React 中,除了把 JSX 當成一個變數 JavaScript 變數直接傳遞之外,更常見的是把 JSX 的內容包成一個 React 元件,至於要怎麼把 JSX 包成 React 元件呢?關於這點,其實你早就會了,就是建立一個函式把 JSX 的內容回傳出來而已。像這樣,我們就建立了一個名為 Counter 的 React 元件:

// 建立一個名為 Counter 的 React 元件
const Counter = () => {
  return (
    <div className="container">
      <div className="chevron chevron-up" />
      <div className="number">256</div>
      <div className="chevron chevron-down" />
    </div>
  );
};

React 是不是很簡單啊XDDD

另外在箭頭函式(arrow function)中,當該函式只是單純回傳某一值時,可以把要回傳的內容直接放到 => 後面而不用額外再寫 return,因此會精簡成這樣:

// 建立一個名為 Counter 的 React 元件
const Counter = () => (
  <div className="container">
    <div className="chevron chevron-up" />
    <div className="number">256</div>
    <div className="chevron chevron-down" />
  </div>
);

將寫好的元件顯示出來

原本我們是在 root.render() 中的參數放入 JSX,現在只需把剛剛建立好的 React 元件當成一個 HTML 標籤(<Counter />)放進去就可以了,像是這樣:

const Counter = () => (
  <div className="container">
    <div className="chevron chevron-up" />
    <div className="number">256</div>
    <div className="chevron chevron-down" />
  </div>
);

const root = ReactDOM.createRoot(document.getElementById('root'));

// 使用 <Counter /> 來帶入 React 元件
root.render(<Counter />);

和原本建立變數後帶進去的寫法差異不大:

Imgur

這時候一樣可以正確地顯示出計數器畫面。完成的程式碼可以參考這份 Day 5 - Counter with React Component 在 CodePen 的連結。

元件有什麼好處

你可能會好奇把原本的 HTML 包成一個 React 元件有什麼好處呢?假設現在碰到一個情況,是需要一個頁面中同時需要很多個計數器的話,像是下圖這樣,可以怎麼做呢?

Imgur

我們當然可以複製同樣的 HTML 到程式碼中,但因為原本的 JavaScript 都是透過 querySelector 來選到 DOM 元素後再對不同的元素去監聽事件和改變數值,因此當我們直接複製三次時,因為 querySelector 會選到重複的元素,所以我們必須要再去修改程式碼才能讓這三個計數器都擁有正常的功能。簡單來說,用原生 JavaScript 來寫絕對做得到,但就是比較麻煩。

而元件的好處在於讓開發者可以輕鬆的重複使用這些元件,當我們需要三個計數器時,只需要使用三次 <Counter /> 就可以了,像是這樣:

const Counter = () => (
  <div className="container">
    <div className="chevron chevron-up" />
    <div className="number">256</div>
    <div className="chevron chevron-down" />
  </div>
);

const root = ReactDOM.createRoot(document.getElementById('root'));

// 重複使用三次 <Counter /> 元件
root.render(
   <div
    style={{
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'space-around',
    }}
  >
    <Counter />
    <Counter />
    <Counter />
  </div>
);

畫面會像這樣:

Imgur

現在雖然已經快速複製出三個計數器,但還沒有增減數字的功能,別擔心這個部分我們很快就會練習到。重複使用元件的程式碼可以參考這個 CodePen 連結: Day 5 - Multiple Counters with React Component

React 中的命名慣例

在 React 中對於元件和 HTML 屬性、CSS 樣式等等有一些命名上的「慣例」,當沒有照著這些慣例來命名時,會出現錯誤的提示,多半時候你只需要知道大致上的規則,當看到錯誤提示時再依據錯誤的提示進行修改就可以了。

React 的「元件名稱」會以大寫駝峰的方式來命名,也就是首字母大寫,例如, Counter,若該名稱由多個單字組成,則把每一單字的第一個字大寫,例如,AdminHeaderPaymentButton。如果沒這麼做的話,React 會把它當作一般的 HTML 元素處理,並跳出錯誤提示。

例如,如果我們把 <Counter /> 改成小寫開頭的 <counter />

Warning: The tag <counter> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.

像是這樣:

Imgur

其他像是 HTML 中的屬性、CSS 樣式屬性或一般的函式來說,則會遵行 JavaScript 以小寫駝峰來命名變數的慣例,例如在 classNamemaxLengthbackgroundColor 等等。

<input type="text" maxlength="10" /> 為例,在 React 的 JSX 中需要把 maxlength 改成 maxLength,不然一樣會拋出錯誤:

<!--
- 在 JSX 中屬性如果由多個單字組成,需要使用小寫駝峰命名,否則會有錯誤訊息
- Warning: Invalid DOM property `maxlength`. Did you mean `maxLength`?
-->
<input type="text" maxlength="10" />

<!-- 正確寫法 -->
<input type="text" maxLength="10" />

像是這樣:

Imgur

使用慣例的好處是當自己或他人看到程式碼時,可以很快從變數的命名了解它可能的類型,例如,當看到以大寫駝峰方式命名的變數時,可以馬上知道這是個 React 元件而非一般的函式。

今天我們學到了一些 JSX 中的基本概念,同時知道怎麼把 CSS 樣式帶入到 JSX 內。當然與 JSX 有關的用法不僅僅是這樣,但如果在這就一條一條列出來應該會蠻枯燥的。所以,關於 JSX 的其它用法,我們會在後面需要用到的時候在一併說明。

程式範例

參考資源


上一篇
[Day 04 - 計數器] 把 HTML 寫在 JavaScript 中!? - JSX 的使用
下一篇
[Day 06 - 計數器] 醒醒啊!為什麼一動也不動 - useState 的基本使用
系列文
從 Hooks 開始,讓你的網頁 React 起來30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

3
谷哥
iT邦新手 3 級 ‧ 2019-09-21 20:41:32

在 React 中,「組件名稱」多以大寫駝峰的方式來命名,也就是首字母大寫

這是在 React 中對於組件和其他屬性命名上的「慣例」

即便你沒有照著慣例走,多數時候程式還是能正常運作

在 React 中元件名稱一定要首字母大寫,這不僅僅是慣例喔,如果首字母小寫,會被 React 當成一般的 HTML 標籤,該元件就無法正常運作了。

HTML 中的屬性、CSS 樣式屬性也都一定要首字母小寫駝峰,不然也是不會正常運作的。

pjchender iT邦新手 3 級 ‧ 2019-09-21 22:52:06 檢舉

謝謝 @谷哥 的提醒,已經將內文做了修改,常常寫得太習慣,都忘了沒照著這樣做的確會有錯誤產生!
非常感謝??

谷哥 iT邦新手 3 級 ‧ 2019-09-21 22:56:15 檢舉

修改好快!期待後面的內容!加油!

我要留言

立即登入留言